import time


TAB = "    "


class BlockComment(object):
    def __init__(self, fmt, *args):
        if args:
            text = fmt % args
        else:
            text = fmt
        text.replace("*/", "* /")
        self.lines = text.splitlines()
    
    def emit(self, file, nesting):
        ind = TAB * nesting
        banner = "*" * (78 - len(ind))
        file.write("%s/%s\n" % (ind, banner))
        for line in self.lines:
            file.write("%s* %s\n" % (ind, line))
        file.write("%s%s/\n" % (ind, banner))

class VarDef(object):
    def __init__(self, type, name, subscripts = []):
        self.type = type
        self.name = name
        self.subs = "".join("[%s]" % (s,) for s in subscripts)
    
    def __str__(self):
        return "%s %s%s" % (self.type, self.name, self.subs)
    
    def emit(self, file, nesting):
        file.write("%s%s;\n" % (TAB * nesting, self))
    
    def get_deps(self):
        return [self.type]

class Typedef(object):
    def __init__(self, oldtype, name, subscripts = []):
        self.oldtype = oldtype
        self.name = name
        self.subs = "".join("[%s]" % (s,) for s in subscripts)
    def __str__(self):
        return self.name
    def declare(self, file):
        file.write("typedef %s %s%s;\n" % (self.oldtype, self.name, self.subs))
    def emit(self, file, nesting):
        pass
    def get_deps(self):
        return [self.oldtype]
    def has_member(self, name):
        return self.oldtype.has_member(name)

class ExternType(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return self.name
    def emit(self, file, nesting):
        pass
    def get_deps(self):
        return []

class Cast(object):
    def __init__(self, expr, type):
        self.expr = expr
        self.type = type
    def __str__(self):
        return "((%s)%s)" % (self.type, self.expr)

class Member(object):
    def __init__(self, type, owner, member):
        self.owner = owner
        self.member = member
        for m in type.members:
            if m.name == member:
                if isinstance(m.type, Ptr):
                    self.op = "->"
                else:
                    self.op = "."
                break
        else:
            raise ValueError("no member named %r" % (member,))
    
    def __str__(self):
        return "%s%s%s" % (self.owner, self.op, self.member)


class Struct(object):
    def __init__(self, name):
        self.name = name
        self.members = []
    
    def __str__(self):
        return str(self.name)
    
    def has_member(self, name):
        for mem in self.members:
            if mem.name == name:
                return True
        return False
    
    def declare(self, file):
        file.write("struct _%s;\n" % (self.name,))
        file.write("typedef struct _%s %s;\n" % (self.name, self.name))
    
    def append(self, type, name, subscripts = []):
        self.members.append(VarDef(type, name, subscripts))
    
    def emit(self, file, nesting):
        ind = TAB * nesting
        file.write("%sstruct _%s\n%s{\n" % (ind, self.name, ind))
        for mem in self.members:
            mem.emit(file, nesting + 1)
        file.write("%s};\n\n" % (ind,))
    
    def get_deps(self):
        return [mem.type for mem in self.members]

class BuiltinType(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return self.name
    def emit(self, file, nesting):
        pass
    def get_deps(self):
        return []
    def has_member(self, name):
        return False

class Ptr(object):
    def __init__(self, type):
        self.type = type
    def __str__(self):
        return str(self.type) + " *"
    def emit(self, file, nesting):
        self.type.emit(file, nesting)
    def get_deps(self):
        return self.type.get_deps()

class Macro(object):
    def __init__(self, name, value, *args):
        self.name = name
        if args:
            self.value = value % args
        else:
            self.value = value
    def emit(self, file, nesting):
        file.write("%s#define %s %s\n" % (TAB * nesting, self.name, self.value))

class Stmt(object):
    def __init__(self, fmt, *args):
        if args:
            self.code = fmt % args
        else:
            self.code = fmt
    
    def emit(self, file, nesting):
        file.write("%s%s;\n" % (TAB * nesting, self.code))

class Block(object):
    def __init__(self, *stmts):
        self.stmts = list(stmts)
    
    def append(self, stmt):
        self.stmts.append(stmt)
    
    def emit(self, file, nesting):
        for stmt in self.stmts:
            stmt.emit(file, nesting)

class ForStmt(object):
    def __init__(self, fmt, *args):
        if args:
            self.code = fmt % args
        else:
            self.code = fmt
        self.body = Block()
    
    def emit(self, file, nesting):
        ind = TAB * nesting
        file.write("%sfor (%s)\n%s{\n" % (ind, self.code, ind))
        self.body.emit(file, nesting + 1)
        file.write("%s}\n" % (ind,))

class WhileStmt(object):
    def __init__(self, fmt, *args):
        if args:
            self.code = fmt % args
        else:
            self.code = fmt
        self.body = Block()
    
    def emit(self, file, nesting):
        ind = TAB * nesting
        file.write("%swhile (%s)\n%s{\n" % (ind, self.code, ind))
        self.body.emit(file, nesting + 1)
        file.write("%s}\n" % (ind,))

class Func(object):
    def __init__(self, name, rettype, *args):
        self.name = name
        self.rettype = rettype
        self.args = args
        self.body = Block()
    
    def declare(self, file):
        file.write("%s %s(%s);\n" % (self.rettype, self.name, 
            ", ".join(str(a) for a in self.args)))
    
    def emit(self, file, nesting):
        ind = TAB * nesting
        file.write("%s%s %s(%s)\n%s{\n" % (ind, self.rettype, self.name, 
            ", ".join(str(a) for a in self.args), ind))
        self.body.emit(file, nesting + 1)
        file.write("%s}\n\n" % (ind,))
    
    def invoke(self, res, *args):
        if res is None:
            return Stmt("%s(%s)", self.name, ", ".join(args))
        else:
            return Stmt("%s = %s(%s)", res, self.name, ", ".join(args))

class ExternFunc(Func):
    def __init__(self, name):
        self.name = name
    def declare(self, file):
        pass
    def emit(self, file, nesting):
        pass
    def invoke(self, res, *args):
        if res is None:
            return Stmt("%s(%s)", self.name, ", ".join(args))
        else:
            return Stmt("%s = %s(%s)", res, self.name, ", ".join(args))

def _calc_depth(type, cache):
    if type in cache:
        return cache[type]
    cache[type] = d = 0
    children = type.get_deps()
    if children:
        d = 1 + max(_calc_depth(child, cache) for child in children)
        cache[type] = d
    return d

class Module(object):
    def __init__(self, name):
        self.name = name
        self.c_file = name + ".c"
        self.h_file = name + ".h"
        self.includes = set()
        self.macros = {}
        self.types = {}
        self.funcs = {}
    
    def add(self, obj):
        if isinstance(obj, Func):
            self.funcs[obj.name] = obj
        if isinstance(obj, (Typedef, Struct)):
            self.types[obj.name] = obj
        if isinstance(obj, Macro):
            self.macros[obj.name] = obj
    
    def generate_header(self):
        f = open(self.h_file, "w")
        cmt = BlockComment("Module Header for: %r\nGenerated on %s" % (self.name, 
            time.asctime()))
        cmt.emit(f, 0)
        f.write("#ifndef %s_H_INCLUDED\n" % (self.name.upper(),))
        f.write("#define %s_H_INCLUDED\n\n" % (self.name.upper(),))
        if self.includes:
            for inc in self.includes:
                f.write("#include %s\n" % (inc,))
            f.write("\n")
        if self.macros:
            for mac in self.macros.values():
                mac.emit(f, 0)
            f.write("\n")
        if self.types:
            cache = {}
            types = sorted((_calc_depth(t, cache), t) for t in self.types.values())
            for depth, typ in types:
                typ.declare(f)
            f.write("\n")
            for depth, typ in types:
                typ.emit(f, 0)
            f.write("\n")
        if self.funcs:
            for func in self.funcs.values():
                func.declare(f)
        f.write("\n")
        f.write("#endif /* %s_H_INCLUDED */\n" % (self.name.upper(),))
        f.close()
    
    def generate_module(self):
        f = open(self.c_file, "w")
        cmt = BlockComment("Module: %r\nGenerated on %s" % (self.name, 
            time.asctime()))
        cmt.emit(f, 0)
        f.write('#include "%s"\n\n' % (self.h_file,))
        for func in self.funcs.values():
            func.emit(f, 0)
        f.close()
    
    def generate(self):
        self.generate_header()
        self.generate_module()


c_void = BuiltinType("void")
c_int = BuiltinType("int")
c_char = BuiltinType("char")
c_short = BuiltinType("short")
c_long = BuiltinType("long")
c_uint = BuiltinType("unsigned int")
c_uchar = BuiltinType("unsigned char")
c_ushort = BuiltinType("unsigned short")
c_ulong = BuiltinType("unsigned long")
c_sint = BuiltinType("signed int")
c_schar = BuiltinType("signed char")
c_sshort = BuiltinType("signed short")
c_slong = BuiltinType("signed long")
c_single = BuiltinType("single")
c_double = BuiltinType("double")
















